package com.xiang.ad.index.interest;

import com.xiang.ad.index.IndexAware;
import com.xiang.ad.utils.CommonUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * Created by xiang.
 * 兴趣 的 index crud
 * 推广单元的限制维度  使用倒排索引，以兴趣的形式寻找推广单元的id，兴趣&推广单元的id之间的关系是多对多的
 */
@Slf4j
@Component
public class UnitItIndex implements IndexAware<String, Set<Long>> {//key是兴趣标签，value是unitId

    // 这里都用set，因为一个兴趣可能关联多个推广单元，一个推广单元也可能关联多个兴趣
    // <itTag, adUnitId set>
    private static Map<String, Set<Long>> itUnitMap;//倒排索引

    // <unitId, itTag set>
    private static Map<Long, Set<String>> unitItMap;//正向索引

    //使用ConcurrentHashMap初始化两个map
    static {
        itUnitMap = new ConcurrentHashMap<>();
        unitItMap = new ConcurrentHashMap<>();
    }

    //通过itTag获取所关联的UnitIds
    @Override
    public Set<Long> get(String key) {
        return itUnitMap.get(key);
    }

    //添加索引
    @Override
    public void add(String key, Set<Long> value) {

        log.info("UnitItIndex, before add: {}", unitItMap);

        //itUnitMap的添加
        Set<Long> unitIds = CommonUtils.getorCreate(
                key,
                itUnitMap,
                ConcurrentSkipListSet::new
        );

        unitIds.addAll(value);

        //unitItMap的添加
        for (Long unitId : value) {

            Set<String> its = CommonUtils.getorCreate(
                    unitId,
                    unitItMap,
                    ConcurrentSkipListSet::new
            );
            its.add(key);
        }

        log.info("UnitItIndex, after add: {}", unitItMap);
    }

    //不允许更新，set遍历成本消耗太高，只允许删除和重新添加
    @Override
    public void update(String key, Set<Long> value) {

        log.error("it index can not support update");
    }

    // 删除索引
    @Override
    public void delete(String key, Set<Long> value) {

        log.info("UnitItIndex, before delete: {}", unitItMap);

        //部分删除
        /**
         * 之所以不直接使用itUnitMap去remove这些key，因为这些key对应的推广单元的ids并不一定是全量的，可以删除一部分兴趣
         * 比如一个itTag对应到推广单元是123,这里就可以删除对应的1和2而不删除3，所以先取出来，然后删除对应的。
         * 如果直接使用itUnitMap删除对应的key，会把3也删除掉
         */
        Set<Long> unitIds = CommonUtils.getorCreate(
                key,
                itUnitMap,
                ConcurrentSkipListSet::new
        );
        unitIds.removeAll(value);

        //部分删除
        for (Long unitId : value) {
            Set<String> itTagSet = CommonUtils.getorCreate(
                    unitId,
                    unitItMap,
                    ConcurrentSkipListSet::new
            );
            itTagSet.remove(key);
        }

        log.info("UnitItIndex, after delete: {}", unitItMap);
    }

    // 匹配
    // 查看推广单元是否包含了所有的兴趣标签，为检索模块做准备
    // 根据兴趣feature对推广单元进行再筛选
    public boolean match(Long unitId, List<String> itTags) {

        //非空判断 key  value是否存在
        if (unitItMap.containsKey(unitId)
                && CollectionUtils.isNotEmpty(unitItMap.get(unitId))) {

            //获取unit关联的所有兴趣标签
            Set<String> unitItTags = unitItMap.get(unitId);

            // 判断itTags是否是unitItTags的子集
            return CollectionUtils.isSubCollection(itTags, unitItTags);
        }

        return false;
    }
}
